/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.custom.websocket.server;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.AttributeKey;
import io.netty.util.internal.StringUtil;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

/**
 * <p>
 * WebSocketFrameHandler
 *
 * </p>
 *
 * @author gobigg
 * @since 2022/1/20
 */
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
    static Map<String, String> users = new HashMap<>(6);
    private static final String ALL = "all";
    static final AttributeKey<String> uidAttributeKey = AttributeKey.valueOf("uid");

    static {
        users.put("token1", "user1");
        users.put("token2", "user2");
        users.put("token3", "user3");
        users.put("token4", "user4");
        users.put("token5", "user5");
        users.put("token6", "user6");
    }


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive:" + ctx.channel().attr(uidAttributeKey).get());

    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelRegistered");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        //断开连接
        String uid = ctx.channel().attr(uidAttributeKey).get();
        ChannelHolder.removeChannel(uid, ctx.channel());
        System.out.println("客户端断开连接,uid=" + uid + ",channel=" + ctx.channel().id().asLongText());
        printUsers();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if(evt instanceof IdleStateEvent) {
            //将  evt 向下转型 IdleStateEvent
            IdleStateEvent event = (IdleStateEvent) evt;
            String eventType;
            switch (event.state()) {
                case READER_IDLE:
                    eventType = "读空闲";
                    break;
                case WRITER_IDLE:
                    eventType = "写空闲";
                    break;
                case ALL_IDLE:
                    eventType = "读写空闲";
                    break;
                default:
                    eventType = "default";
                    break;
            }
            System.out.println(ctx.channel().remoteAddress() + "--超时时间--" + eventType);
            System.out.println("服务器做相应处理..");
            //空闲之后发送ping message
            //ctx.channel().writeAndFlush(new PingWebSocketFrame());
            //如果发生空闲，我们关闭通道
             ctx.channel().close();
        }
        if (evt instanceof WebSocketServerProtocolHandler.ServerHandshakeStateEvent) {
            String name = ((WebSocketServerProtocolHandler.ServerHandshakeStateEvent) evt).name();
            System.out.println("WebSocketServerProtocolHandler.name=" + name);
        }
        if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
            String requestUri = ((WebSocketServerProtocolHandler.HandshakeComplete) evt).requestUri();
            System.out.println("HandshakeComplete.requestUri=" + requestUri);
            HttpHeaders httpHeaders = ((WebSocketServerProtocolHandler.HandshakeComplete) evt).requestHeaders();
            String token = httpHeaders.get("token");
            token = "token1";
            if (StringUtil.isNullOrEmpty(token) || StringUtil.isNullOrEmpty(users.get(token))) {
                //如果没有token则关闭
                ctx.channel().writeAndFlush(new TextWebSocketFrame("没有权限"));
                ctx.channel().close();
            } else {
                ctx.channel().attr(uidAttributeKey).set(users.get(token));
                //保存uid和
                ChannelHolder.addChannel(users.get(token), ctx.channel());
                ctx.channel().writeAndFlush("channelActive,你好：" + new TextWebSocketFrame(ctx.channel().attr(uidAttributeKey).get()));
                printUsers();
            }

        }
        super.userEventTriggered(ctx, evt);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
        // ping and pong frames already handled
        if (frame instanceof TextWebSocketFrame) {
            // Send the uppercase string back.
            String toUser = ((TextWebSocketFrame) frame).text();
            System.out.println("收到消息" + toUser);

            if (ALL.equalsIgnoreCase(toUser)) {
                ChannelHolder.send2All((new TextWebSocketFrame(ctx.channel().attr(uidAttributeKey) + "发送给" + ALL + "的消息")));
            } else {
                Channel toUserChannel = ChannelHolder.findChannel(toUser);
                if (null == toUserChannel) {
                    ctx.channel().writeAndFlush(new TextWebSocketFrame("用户" + toUser + "不在线"));
                } else {
                    toUserChannel.writeAndFlush(new TextWebSocketFrame(ctx.channel().attr(uidAttributeKey) + "发送给" + toUser + "的消息"));
                    ctx.channel().writeAndFlush(new TextWebSocketFrame("发送" + toUser + "完毕"));
                }
            }

        } else if (frame instanceof PongWebSocketFrame) {
            PongWebSocketFrame pongWebSocketFrame = (PongWebSocketFrame) frame;
            System.out.println(pongWebSocketFrame);
        } else {
            String message = "unsupported frame type: " + frame.getClass().getName();
            throw new UnsupportedOperationException(message);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        Channel channel = ctx.channel();
        if (channel.isActive()) {
            ctx.close();
        }
        cause.printStackTrace();
    }

    void printUsers() {
        System.out.println("当前在线用户:" + ChannelHolder.getChannelMap().keySet().toString());
    }
}
